/**
 * @fileoverview Classes for Icodeon AJAX XML Support
 * @author Icodeon Ltd mailto:sales@icodeon.com
 * @license Apache License 2.0
 * @version 1.0
 */
var icodeon;
if(!icodeon){icodeon = {};}
else if(typeof icodeon != "object"){throw new Error("namepsace icodeon exists");}
if(!icodeon.xml){icodeon.xml = {};}
else if(typeof icodeon.xml != "object"){throw new Error("namepsace icodeon.xml exists");}

/**
 * Construct an Error subclass for Icodeon XML exceptions
 * 
 * @class This class represents an instance of XmlError.
 * @extends Error
 * @constructor
 * @return A new instance of XmlError
 */	
icodeon.xml.XmlError = function(str_Message){

	/**
	 * Method to call the superclass.
	 */
	Error.call(this, str_Message);
	
	this.message = str_Message;

	this.toString = function(){
		var str_Msg = "Type: icodeon.xml.XmlError \n";
		str_Msg += "message: " + this.message + "\n";
		return str_Msg;
	};
};

// XmlError properties
icodeon.xml.XmlError.prototype = new Error();
icodeon.xml.XmlError.prototype.constructor = icodeon.xml.XmlError;

/**
 * Construct a Serializer
 * 
 * @class The class represents an XML serializer utility
 * @constructor
 * @return A new instance of an XML serializer utility
 */
icodeon.xml.Serializer = function(){
	
	/**
	 * 
	 * @param {Node} obj_Node
	 * @return The node as a string
	 * @type String
	 */
	this.serialize = function(obj_Node){
		if(window.XMLSerializer){
			return new XMLSerializer().serializeToString(obj_Node);
		}
		else{
			return obj_Node.xml;
		}
	};
	
	/**
	 * 
	 * @param {String} str_Text
	 * @return 
	 * @type Node
	 */
	this.deserialize = function (str_Text){
	};
};

/**
 * @param {Node} obj_Node
 * @return The node serialized to a string
 * @type String
 * @throws icodeon.xml.XmlError
 */
icodeon.xml.serialize = function(obj_Node){
	if(!window.XMLSerializer){
		var obj_Serializer
	}
	else{
		var str_Xml = obj_Node.xml;
	}
};


icodeon.xml.deserialize = function(str_Text){
};

/**
 *@constrcutor
 */
icodeon.xml.XPathExpression = function(str_XPath, arr_NameSpaces){
	this.str_XPath = str_XPath;
	this.arr_NameSpaces = arr_NameSpaces;
	this.str_NameSpaces = undefined;

	if(!document.createExpression){
		// IE
		this.str_NameSpaces = "";
		if(arr_NameSpaces){
			for(var str_Prefix in this.arr_NameSpaces){
				if(this.str_NameSpaces){
					this.str_NameSpaces += " ";
				}	
				var str_Delim = "";
				if(str_Prefix.length > 0){
					str_Delim = ":";
				}
	
				this.str_NameSpaces += "xmlns" + str_Prefix + str_Delim + "='" + this.arr_NameSpaces[str_Prefix] + "'";
			}
		}
	}
	
	/**
	 * Returns an array of nodes from the evaluation of the Xpath expression
	 * @param {Node} obj_Node element or document node
	 * @return Array of nodes from the evaluation of XPath
	 * @type Array
	 */
	this.getNodes = function(obj_Node){
		if(!obj_Node){
			throw new icodeon.xml.XmlError("Cannot evaluate XPath expression for undefined node: " + obj_Node);
		}
		if(obj_Node.nodeType != 9 && obj_Node.nodeType != 1){
			// Only support element or document nodes
			throw new icodeon.xml.XmlError("Cannot evaluate XPath expression for node type: " + obj_Node.nodeType);
		}
		
		var obj_DomDocument = undefined;
		if(obj_Node.nodeType == 9){
			obj_DomDocument = obj_Node;
		}
		else{
			obj_DomDocument = obj_Node.ownerDocument;
		}
		
		if(document.createExpression){
			// W3C compliant
			try{
				var fn = function(str_Prefix){return this.arr_NameSpaces[str_Prefix];}
				var obj_XPathExpression = obj_DomDocument.createExpression(this.str_XPath, fn);
				var obj_XPathResult = this.obj_XPathExpression.evaluate(obj_Node, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
				var int_NodeCount = obj_XPathResult.snapshotLength;
				var arr_Nodes = new Array(int_NodeCount);
				for(var i=0; i<int_NodeCount; i++){
					arr_Nodes[i] = obj_XPathResult.snapshotItem(i);
				}
				return arr_Nodes;
			}
			catch(obj_Error){
				throw new icodeon.xml.XmlError(obj_Error);
			}
		}
		else{
			// IE
			try{
				obj_DomDocument.setProperty("SelectionLanguage","XPath");
				obj_DomDocument.setProperty("SelectionNamespaces", this.str_NameSpaces);
				if(obj_Node.nodeType == 9){
					obj_Node = obj_Node.documentElement;
				}
				return obj_Node.selectNodes(this.str_XPath);
			}
			catch(obj_Error){
				throw new icodeon.xml.XmlError("Cannot evaluate XPath expression in browser: " + navigator.appName);
			}
		}
	};
	
	/**
	 * Returns a single node from the evaluation of the Xpath expression (or null)
	 * @param {Node} obj_Node element or document node
	 * @return Single node from the evaluation of XPath
	 * @type Node
	 */
	this.getNode = function(obj_Node){
		if(!obj_Node){
			throw new icodeon.xml.XmlError("Cannot evaluate XPath expression for undefined node: " + obj_Node);
		}
		if(obj_Node.nodeType != 9 && obj_Node.nodeType != 1){
			// Only support element or document nodes
			throw new icodeon.xml.XmlError("Cannot evaluate XPath expression for node type: " + obj_Node.nodeType);
		}

		var obj_DomDocument = undefined;
		if(obj_Node.nodeType == 9){
			obj_DomDocument = obj_Node;
		}
		else{
			obj_DomDocument = obj_Node.ownerDocument;
		}
		
		if(document.createExpression){
			// W3C compliant
			try{
				var fn = function(str_Prefix){return this.arr_NameSpaces[str_Prefix];}
				var obj_XPathExpression = obj_DomDocument.createExpression(this.str_XPath, fn);
				var obj_XPathResult = obj_XPathExpression.evaluate(obj_Node, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
				return obj_XPathResult.singleNodeValue;
			}
			catch(obj_Error){
				throw new icodeon.xml.XmlError(obj_Error);
			}
		}
		else{
			// IE
			try{
				var obj_DomDocument = undefined;
				if(obj_Node.nodeType == 9){
					obj_DomDocument = obj_Node;
				}
				else{
					obj_DomDocument = obj_Node.ownerDocument;
				}

				obj_DomDocument.setProperty("SelectionLanguage","XPath");
				obj_DomDocument.setProperty("SelectionNamespaces", this.str_NameSpaces);
				if(obj_Node.nodeType == 9){
					obj_Node = obj_Node.documentElement;
				}
				return obj_Node.selectSingleNode(this.str_XPath);
			}
			catch(obj_Error){
				throw new icodeon.xml.XmlError("Cannot evaluate XPath expression in browser: " + navigator.appName);
			}
		}
	};
};


/**
 * @param {Element} obj_Element
 * @param {String} str_Text
 * @throws icodeon.xml.XmlError
 */
icodeon.xml.setElementText = function(obj_Element,str_Text){
	if(obj_Element.nodeType == 1){
		if(obj_Element.firstChild && obj_Element.firstChild.nodeType==3){
			obj_Element.firstChild.nodeValue = str_Text;
		}
		else{
			var obj_TextNode = obj_Element.ownerDocument.createTextNode(str_Text);
			obj_Element.appendChild(obj_TextNode);
		}
	}
	else{
		throw new icodeon.xml.XmlError("Node is not an element: node type=" + obj_Element.nodeType)
	}
};

/**
 * @param {Element} obj_Element
 * @throws icodeon.xml.XmlError
 */
icodeon.xml.getElementText = function(obj_Element){
	if(obj_Element.nodeType == 1){
		if(obj_Element.firstChild && obj_Element.firstChild.nodeType==3){
			return obj_Element.firstChild.nodeValue;
		}
		else{
			return "";
		}
	}
	throw new icodeon.xml.XmlError("Node is not an element: node type=" + obj_Element.nodeType)

};

/**
 * @param {Node} obj_ParentNode
 * @param {String} str_ChildName the child element name
 * @param {String} str_ChildValue the child element text node
 * @param {String} str_Namespace
 * @return The child element
 * @type Element
 * @throws icodeon.xml.XmlError
 */
icodeon.xml.appendChildElement = function(obj_ParentNode, str_ChildName, str_ChildValue, str_Namespace){
	var obj_Child = undefined;
	var obj_DomDocument = obj_ParentNode.ownerDocument;	

    if (document.createElementNS) {
        if (str_Namespace) {
            obj_Child = obj_DomDocument.createElementNS(str_Namespace, str_ChildName);
        } else {
            obj_Child = obj_DomDocument.createElement(str_ChildName);
        }
    }
    else if (document.createElement) {
        obj_Child = obj_DomDocument.createElement(str_ChildName);
    }
    else{
        throw new icodeon.xml.XmlError("Cannot create child element for name: " + str_ChildName);
    }
    
    if(str_ChildValue){
   		var obj_TextNode = obj_DomDocument.createTextNode(str_ChildValue);
   		obj_Child.appendChild(obj_TextNode);
		obj_ParentNode.appendChild(obj_Child);
    }
    else{
		obj_ParentNode.appendChild(obj_Child);
    }
    return obj_Child;
};

/**
 * @param {String} str_RootElementName
 * @param {String} str_NamespaceUri the default namespace
 * @return The DOM document
 * @type XML Document
 * @throws icodeon.xml.XmlError
 */
icodeon.xml.createDomDocument = function(str_RootElementName, str_NamespaceUri){

	if(document.implementation && document.implementation.createDocument){
		// for standards based browsers  (FF, Safari etc)
		return document.implementation.createDocument(str_NamespaceUri,str_RootElementName,null);
	}
	else{
		// for MSIE
		var arr_ProgIDs = ['Msxml2.DOMDocument.6.0', 'Msxml2.DOMDocument.5.0', 'Msxml2.DOMDocument.4.0', 'Msxml2.DOMDocument.3.0', 'Msxml2.DOMDocument' ];
		var obj_DomDocument = undefined;
		for (var i = 0; i < arr_ProgIDs.length; i++) {
            try {
                obj_DomDocument = new ActiveXObject(arr_ProgIDs[i]);
            }
            catch (obj_Error) {
            }
        }
        if(!obj_DomDocument){
        	throw new icodeon.xml.XmlError("Cannot instaniate a DOM document using Microsoft Active X");
        }

			
        if(str_NamespaceUri){
           obj_DomDocument.loadXML('<' + str_RootElementName + " xmlns=\"" + str_NamespaceUri + "\"" + " />");
        } 
        else {
           obj_DomDocument.loadXML('<' + str_RootElementName + " />");
		}
		return obj_DomDocument;
	}
};

/**
 * Use when the content of an XML text node contains
 * characters that are illegal in XML 1.0, such as ASCII 
 * control characters.
 * 
 * @param {String} str_Value
 * @return The encoded string
 * @type String
 */
icodeon.xml.encode = function(str_Value){
	var str_Encoded = undefined;
	try{
		str_Encoded = escape( encodeURIComponent(str_Value));
	}
	catch(obj_Error){
		try{
			str_Encoded = escape(str_Value);
		}
		catch(obj_Error2){
			str_Encoded = str_Value;
		}
	}
	return str_Encoded;
};

/**
 * Use when the content of an XML text node has
 * been processed with the icodeon.xml.encode function
 * @param {String} str_Value
 * @return The decoded string
 * @type String
 */
icodeon.xml.decode = function(str_Value){
	var str_Decoded = str_Value;
	try{
		str_Decoded = decodeURIComponent(unescape(str_Value));
	}
	catch(obj_Error){
		try{
			str_Decoded = unescape(str_Value);
		}
		catch(obj_Error2){
			str_Decoded = str_Value;
		}
	}
	return str_Decoded;
};

